﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using Newtonsoft.Json.Linq;
using System.Threading;

namespace Core
{
    public class Search
    {
        /// <summary>
        /// Perform a search for the given string on Google using Google Custom Search API.
        /// Custom search API uses REST to transmit data.
        /// Each 10 results you want is a new search.  So use > 10 results sparingly.
        /// </summary>
        /// <param name="searchString">The string to search.  This will be escaped, so don't worry about needing to do it yourself.</param>
        /// <param name="numberResults">Number of results to return (10 to 100).  Must be multiple of 10.</param>
        /// <returns>An array of all the URLs returned.</returns>
        public List<List<SearchResult>> PerformSearch(string searchString, int numberResults)
        {
            //TODO -> action needed
            string apiKey = "put_your_api_key_here";
            
            searchString = EscapeUriDataStringRfc3986(searchString);

            List<List<SearchResult>> links = new List<List<SearchResult>>();

            //1. Stack Overflow
            links.Add(searchIndividualSite(searchString, numberResults, apiKey, "put_your_key_for_StackOverflow"));
            
            //2. Devshed
            links.Add(searchIndividualSite(searchString, numberResults, apiKey, "put_your_key_for_Devshed"));
            
            //3. CodeGuru
            links.Add(searchIndividualSite(searchString, numberResults, apiKey, "put_your_key_for_CodeGuru"));
            
            //4. Bytes
            links.Add(searchIndividualSite(searchString, numberResults, apiKey, "put_your_key_for_Bytes"));
            
            //5. Daniweb
            links.Add(searchIndividualSite(searchString, numberResults, apiKey, "put_your_key_for_Daniweb"));
            
            return links;
        }

        private List<SearchResult> searchIndividualSite(string searchString, int numberResults, string apiKey, string controlPanelId)
        {
            int start = 1;
            List<SearchResult> links = new List<SearchResult>();

            while (numberResults >= 10)
            {
                HttpWebRequest req = WebRequest.Create("https://www.googleapis.com/customsearch/v1?key=" +
                    apiKey + "&cx=" + controlPanelId + "&q=" + searchString + "&start=" + start) as HttpWebRequest;
                string result = null;
                using (HttpWebResponse resp = req.GetResponse()
                                              as HttpWebResponse)
                {
                    StreamReader reader =
                        new StreamReader(resp.GetResponseStream());
                    result = reader.ReadToEnd();
                }

                //Convert the result into a List of urls of the sites we want
                JObject j = JObject.Parse(result);
                int index = 0;

                while (true)
                {
                    try
                    {
                        SearchResult sr = new SearchResult();
                        sr.Url = (string)j["items"][index]["link"];
                        sr.Title = (string)j["items"][index]["title"];
                        sr.Preview = (string)j["items"][index]["snippet"];
                        links.Add(sr);
                        index++;
                    }
                    catch (Exception e)
                    {
                        break;
                    }
                }

                start += 10;
                numberResults -= 10;
            }

            return links;
        }

        /// <summary>
        /// This is taken from: 
        /// http://stackoverflow.com/questions/846487/how-to-get-uri-escapedatastring-to-comply-with-rfc-3986
        /// 
        /// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986.
        /// </summary>
        private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };

        /// <summary>
        /// This is taken from: 
        /// http://stackoverflow.com/questions/846487/how-to-get-uri-escapedatastring-to-comply-with-rfc-3986
        /// 
        /// Escapes a string according to the URI data string rules given in RFC 3986.
        /// </summary>
        /// <param name="value">The value to escape.</param>
        /// <returns>The escaped value.</returns>
        /// <remarks>
        /// The <see cref="Uri.EscapeDataString"/> method is <i>supposed</i> to take on
        /// RFC 3986 behavior if certain elements are present in a .config file.  Even if this
        /// actually worked (which in my experiments it <i>doesn't</i>), we can't rely on every
        /// host actually having this configuration element present.
        /// </remarks>
        internal static string EscapeUriDataStringRfc3986(string value)
        {
            // Start with RFC 2396 escaping by calling the .NET method to do the work.
            // This MAY sometimes exhibit RFC 3986 behavior (according to the documentation).
            // If it does, the escaping we do that follows it will be a no-op since the
            // characters we search for to replace can't possibly exist in the string.
            StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value));

            // Upgrade the escaping to RFC 3986, if necessary.
            for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++)
            {
                escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
            }

            // Return the fully-RFC3986-escaped string.
            return escaped.ToString();
        }
    }
}
